


#include <stdlib.h>
#include <string.h>
#include <sstream>
#include "category.h"
#include "expression.h"



using namespace std;



const char *ordinal( uint8_t num )
{
    const uint8_t SIZE = 11; 
    static const char *names[ SIZE ] = { "zero", "one", "two", "three", "four", "five", "six", "seven", "eight", "nine", "ten" };
    
    if( num < SIZE )
        return names[num];
    else
        return NULL;
}



void capitalize( ostream &S, const char *str )
{
    if( str[0] )
        S << char(toupper( str[0] )) << &str[1];    
}
    
    

// Expression ::



Expression::Expression()
    : m_bInverted( false )
{
}



bool Expression::straight( Subject *owner ) const
{
    return owner ? isInverted() != owner->tellsTruth() : !isInverted();
}



// ExpressionCategory ::



ExpressionCategory::ExpressionCategory( Category *cat )
    : m_pCategory( cat )
{
    m_pCategory->ref();
}



ExpressionCategory::ExpressionCategory( const ExpressionCategory &exp )
    : Expression( exp )
    , m_pCategory( exp.m_pCategory )
{
    m_pCategory->ref();
}



ExpressionCategory::~ExpressionCategory()
{
    m_pCategory->deref();
}



// ExpAllTellTruth ::



ExpAllTellTruth::ExpAllTellTruth( Category *cat, Subject *owner ) :
    ExpressionCategory( cat )    
{
    m_bInverted = !check(owner);
}



ExpAllTellTruth::ExpAllTellTruth( Category *cat )
    : ExpressionCategory( cat )    
{
}



Expression *ExpAllTellTruth::createExpression( Category *cat, Subject *owner )
{
    return new ExpAllTellTruth(cat, owner);
}



bool ExpAllTellTruth::check( Subject *owner ) const
{
    const CollectionSubjects &doors = m_pCategory->getSubjects(owner);
    CollectionSubjects::const_iterator id = doors.begin(), id_end = doors.end();
    for( ; id != id_end; ++id )
    {
        if( !(*id)->tellsTruth() )
            return !straight(owner);        
    }
    
    return straight(owner);
}



void ExpAllTellTruth::putText( ostream &S, Subject *owner, bool invert ) const
{
    if( isInverted() != invert )
    {
        if( m_pCategory->one(owner) )
        {
            ostringstream oss;
            oss << m_pCategory->putText(owner) << ( m_pCategory->personal() ? " am lying." : " is lying.") << endl;
            capitalize( S, oss.str().c_str() );
        }
        else
        {
            S << "At least one of " << m_pCategory->putText(owner) << " is lying." << endl;
        }
    }
    else
    {
        if( m_pCategory->one(owner) )
        {
            ostringstream oss;
            oss << m_pCategory->putText(owner) << ( m_pCategory->personal() ? " tell truth." : " tells truth.") << endl;
            capitalize( S, oss.str().c_str() );
        }
        else
        {
            S << "All " << m_pCategory->putText(owner) << " are telling truth." << endl;
        }
    }
}



// ExpAllLie ::



ExpAllLie::ExpAllLie( Category *cat, Subject *owner )
    : ExpAllTellTruth(cat)
{
    m_bInverted = !check(owner);    
}



Expression *ExpAllLie::createExpression( Category *cat, Subject *owner )
{
    return new ExpAllLie( cat, owner );
}



bool ExpAllLie::check( Subject *owner ) const
{
    const CollectionSubjects &doors = m_pCategory->getSubjects(owner);
    CollectionSubjects::const_iterator id = doors.begin(), id_end = doors.end();
    for( ; id != id_end; ++id )
    {
        if( (*id)->tellsTruth() )
            return !straight(owner);
    }
    
    return straight(owner);
}



void ExpAllLie::putText( ostream &S, Subject *owner, bool invert ) const
{
    if( isInverted() != invert )
    {
        if( m_pCategory->one(owner) )
        {
            ostringstream oss;
            oss << m_pCategory->putText(owner) << (m_pCategory->personal() ? " tell truth." : " tells truth.") << endl;
            capitalize( S, oss.str().c_str() );
        }
        else
        {
            S << "At least one of " << m_pCategory->putText(owner) << " tells truth." << endl;
        }
    }
    else
    {
        if( m_pCategory->one(owner) )
        {
            ostringstream oss;
            oss << m_pCategory->putText(owner) << (m_pCategory->personal() ? " am lying." : " is lying.") << endl;
            capitalize( S, oss.str().c_str() );
        }
        else
        {
            S << "All " << m_pCategory->putText(owner) << " are lying." << endl;
        }
    }
}



// ExpTellTruthOrLie ::



ExpTellTruthOrLie::ExpTellTruthOrLie( Category *cat, Subject *owner )
    : ExpressionCategory(cat)    
{
    m_bInverted = !check(owner);
}



Expression *ExpTellTruthOrLie::createExpression( Category *cat, Subject *owner )
{
    if( cat->visible() && cat->one() )
        return NULL;
    else
        return new ExpTellTruthOrLie(cat, owner);
}



bool ExpTellTruthOrLie::check( Subject *owner ) const
{
    bool bTruth = false, bLie = false;
    const CollectionSubjects &doors = m_pCategory->getSubjects(owner);
    CollectionSubjects::const_iterator id = doors.begin(), id_end = doors.end();
    if( id == id_end )
        return true;

    for( ; id != id_end; ++id )
    {
        if( (*id)->tellsTruth() )
        {
            bTruth = true;
            if( bLie )
                return straight(owner);
        }
        else
        {
            bLie = true;
            if( bTruth )
                return straight(owner);
        }
    }
    
    return !straight(owner);    
}



void ExpTellTruthOrLie::putText( ostream &S, Subject *owner, bool invert ) const
{
    if( isInverted() != invert )
        S << "Either all " << m_pCategory->putText(owner) << " tell truth, or they all are lying." << endl;
    else
        S << "Some of " << m_pCategory->putText(owner) << " tell truth, some are lying." << endl;
}
